#pragma once

#include <iostream>
#include <queue>
#include <unistd.h>
#include "Cond.hpp" // 引入条件变量
#include "Mutex.hpp" // 引入互斥锁

namespace BlockQueueModule
{

    // 版本 2 

    // 这两个很重要，需要先声明命名空间
    using namespace LockModule;
    using namespace CondModule;

    static const int gcap = 10;

    template <typename T>
    class BlockQueue
    {
    private:
        // 判断对象为空为满，本身就是访问临界资源
        bool IsFull() {return _q.size() == _cap;}
        bool IsEmpty() {return _q.empty();}

    public:
        BlockQueue(int cap = gcap) :_cap(cap), _cwait_num(0), _pwait_num(0)
        {}

        void Equeue(const T &in)  // 生产者
        {
            LockGuard lockguard(_mutex);
            // 注意：这里并不是我们想要放数据就可以去放数据的，生产数据都是有条件的
            // 结论1: 在临界区中等待是必然的（目前）
            while(IsFull()) // 5. 不建议用 if, 对条件进行判定，防止伪唤醒
            {
                std::cout << "生产者进入等待..." << std::endl;

                // 2. 等待是，然后释放 _mutex
                _pwait_num++;
                _productor_cond.Wait(_mutex); // 在临界区中等待是必然的
                _pwait_num--;                
                // 3. 返回，线程被唤醒 && 重新申请并持有锁（它会在临界区醒来）

                std::cout << "生产者被唤醒..." << std::endl;
            }
            // 4. if(Full()) 不满足 || 线程被唤醒
            _q.push(in); // 生产

            // 肯定有数据
            if(_cwait_num)
            {
                std::cout << "叫醒消费者" << std::endl;
                _consumer_cond.Notify();
            }
        }

        void Pop(T *out) // 消费者
        {
            LockGuard lockguard(_mutex);
            while(IsEmpty()) 
            {
                std::cout << "消费者进入等待..." << std::endl;
                _cwait_num++;
                _consumer_cond.Wait(_mutex); // 伪唤醒：在条件不满足的情况被唤醒
                _cwait_num--;
                std::cout << "消费者被唤醒..." << std::endl;
            }
            // 4. if(Ispty()) 不满足 || 线程被唤醒
            *out = _q.front();
            _q.pop();

            // 肯定有空间
            if(_pwait_num)
            {
                std::cout << "叫醒生产者" << std::endl;
                _productor_cond.Notify();
            }
        }

        ~BlockQueue()
        {}
        
    private:
        std::queue<T> _q;  // 保存数据的容器，临界资源
        int _cap; // bq 最大容量
        Mutex _mutex; // 互斥
        Cond _productor_cond; // 生产者条件变量
        Cond _consumer_cond; // 消费者条件变量

        int _cwait_num;
        int _pwait_num;
    };














    // // 版本 1

    // static const int gcap = 10;

    // template <typename T>
    // class BlockQueue
    // {
    // private:
    //     // 判断对象为空为满，本身就是访问临界资源
    //     bool IsFull() {return _q.size() == _cap;}
    //     bool IsEmpty() {return _q.empty();}

    // public:
    //     BlockQueue(int cap = gcap) :_cap(cap), _cwait_num(0), _pwait_num(0)
    //     {
    //         pthread_mutex_init(&_mutex, nullptr);
    //         pthread_cond_init(&_productor_cond, nullptr);
    //         pthread_cond_init(&_consumer_cond, nullptr);
    //     }

    //     void Equeue(const T &in)  // 生产者
    //     {
    //         pthread_mutex_lock(&_mutex);
    //         // 注意：这里并不是我们想要放数据就可以去放数据的，生产数据都是有条件的
    //         // 结论1: 在临界区中等待是必然的（目前）
    //         while(IsFull()) // 5. 不建议用 if, 对条件进行判定，防止伪唤醒
    //         {
    //             std::cout << "生产者进入等待..." << std::endl;

    //             // 2. 等待是，然后释放 _mutex
    //             _pwait_num++;
    //             pthread_cond_wait(&_productor_cond, &_mutex); // 在临界区中等待是必然的
    //             _pwait_num--;                
    //             // 3. 返回，线程被唤醒 && 重新申请并持有锁（它会在临界区醒来）

    //             std::cout << "生产者被唤醒..." << std::endl;
    //         }
    //         // 4. if(Full()) 不满足 || 线程被唤醒
    //         _q.push(in); // 生产

    //         // 肯定有数据
    //         if(_cwait_num)
    //         {
    //             std::cout << "叫醒消费者" << std::endl;
    //             pthread_cond_signal(&_consumer_cond);
    //         }

    //         pthread_mutex_unlock(&_mutex);
    //         // TODO
    //     }

    //     void Pop(T *out) // 消费者
    //     {
    //         pthread_mutex_lock(&_mutex);
    //         // 线程伪唤醒（Thread Spurious Wakeup） 是指在多线程程序中，某个线程在本应处于阻塞状态（如等待条件变量、信号量、互斥锁等）时，
    //         // 可能会无故被唤醒，而并非因为真正的条件满足。这种唤醒没有实际的意义，需要线程在唤醒后进行检查，以确保执行正确的操作
    //         while(IsEmpty()) // 5. 不建议用 if, 对条件进行判定，防止伪唤醒
    //         {
    //             std::cout << "消费者进入等待..." << std::endl;
    //             _cwait_num++;
    //             pthread_cond_wait(&_consumer_cond, &_mutex); // 伪唤醒：在条件不满足的情况被唤醒
    //             _cwait_num--;
    //             std::cout << "消费者被唤醒..." << std::endl;
    //         }
    //         // 4. if(Ispty()) 不满足 || 线程被唤醒
    //         *out = _q.front();
    //         _q.pop();

    //         // 肯定有空间
    //         if(_pwait_num)
    //         {
    //             std::cout << "叫醒生产者" << std::endl;
    //             pthread_cond_signal(&_productor_cond);
    //         }

    //         pthread_mutex_unlock(&_mutex);
    //     }

    //     ~BlockQueue()
    //     {
    //         pthread_mutex_destroy(&_mutex);
    //         pthread_cond_destroy(&_productor_cond);
    //         pthread_cond_destroy(&_consumer_cond);
    //     }
    // private:
    //     std::queue<T> _q;  // 保存数据的容器，临界资源
    //     int _cap; // bq 最大容量
    //     pthread_mutex_t _mutex; // 互斥
    //     pthread_cond_t _productor_cond; // 生产者条件变量
    //     pthread_cond_t _consumer_cond; // 消费者条件变量

    //     int _cwait_num;
    //     int _pwait_num;
    // };

}